﻿/*
    a03_drawing -

    derived from gtk example.
    几个要点,
        .cairo的基本绘制
        .mouse event
*/
#include <gtk/gtk.h>

static cairo_surface_t *surface = NULL;

static void clear_surface(void)
{
    cairo_t *cr = cairo_create(surface);
    cairo_set_source_rgb(cr, 1, 1, 1);
    cairo_paint(cr);
    cairo_destroy(cr);    
}

static gboolean config_event_cb(GtkWidget* widget
                                , GdkEventConfigure *event
                                , gpointer user_data)
{
    if(surface)
        cairo_surface_destroy(surface);

    surface = gdk_window_create_similar_surface(gtk_widget_get_window(widget)
                                                    , CAIRO_CONTENT_COLOR
                                                    , gtk_widget_get_allocated_width(widget)
                                                    , gtk_widget_get_allocated_height(widget));
    clear_surface();

    //this event is handled, no further processing
    return TRUE;
}


static gboolean draw_cb(GtkWidget* widget
                            , cairo_t *cr
                            , gpointer data)
{
    cairo_set_source_surface(cr, surface, 0, 0);
    cairo_paint(cr);

    //propagate the event further
    return FALSE;
}

static void draw_brush(GtkWidget *widget, gdouble x, gdouble y)
{
    cairo_t *cr = cairo_create(surface);
    cairo_rectangle(cr, x-0.5, y-0.5, 1, 1);
    cairo_fill(cr);    
    cairo_destroy(cr);

    gtk_widget_queue_draw(widget);
}

static gboolean button_press_event_cb(GtkWidget *widget
                                        , GdkEventButton *event
                                        , gpointer data)
{
    //in case we get no configure-event
    if(surface == NULL)
        return FALSE;

    if(event->button == GDK_BUTTON_PRIMARY)
    {
        draw_brush(widget, event->x, event->y);
    }
    else if (event->button == GDK_BUTTON_SECONDARY)
    {
        clear_surface();
        gtk_widget_queue_draw(widget);
    }

    //no further processing
    return TRUE;
}

static gboolean motion_notify_event_cb(GtkWidget *widget
                                        , GdkEventMotion *event
                                        , gpointer data)
{
    //in case we get no configure-event
    if(surface == NULL)
        return FALSE;

    if(event->state & GDK_BUTTON1_MASK)
        draw_brush(widget, event->x, event->y);

    //stop propagate
    return TRUE;
}


static void close_window(void)
{
    if(surface)
        cairo_surface_destroy(surface);
}

static void activate(GtkApplication *app, gpointer *user_data)
{
    GtkWidget *window;
    GtkWidget *frame;
    GtkWidget *drawing_area;

    window = gtk_application_window_new(app);
    gtk_window_set_title(GTK_WINDOW(window), "Drawing");
    g_signal_connect(window, "destroy", G_CALLBACK(close_window), NULL);
    gtk_container_set_border_width(GTK_CONTAINER(window), 0);

    frame = gtk_frame_new(NULL);
    gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
    gtk_container_add(GTK_CONTAINER(window), frame);

    drawing_area = gtk_drawing_area_new();
    gtk_widget_set_size_request(drawing_area, 300, 200);
    gtk_container_add(GTK_CONTAINER(frame), drawing_area);

    g_signal_connect(drawing_area, "configure-event", G_CALLBACK(config_event_cb), NULL);
    g_signal_connect(drawing_area, "draw", G_CALLBACK(draw_cb), NULL);
    g_signal_connect(drawing_area, "button-press-event", G_CALLBACK(button_press_event_cb), NULL);
    g_signal_connect(drawing_area, "motion-notify-event", G_CALLBACK(motion_notify_event_cb), NULL);

    gtk_widget_set_events(drawing_area, gtk_widget_get_events(drawing_area)
                                            | GDK_BUTTON_PRESS_MASK
                                            | GDK_BUTTON_MOTION_MASK);
    gtk_widget_show_all(window);
}

int main(int argc, char** argv)
{
    GtkApplication *app;
    int status;

    app = gtk_application_new("com.gitee.zcatt.gnome.example.a03_drawing", G_APPLICATION_FLAGS_NONE);
    g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
    status = g_application_run(G_APPLICATION(app), argc, argv);
    g_object_unref(app);
    return status;
}